#include <kernel.h>

volatile uint32_t             irq_save_level;
volatile static uint32_t      irq_save_bitmap;
volatile static uint32_t      irq_save_buffer[IRQ_COUNT];
volatile static irq_handler_f irq_handler[IRQ_COUNT];
volatile static int           irq_auto_eoi[IRQ_COUNT];

#if 1
#define CALL_IRQ_HANDLER(n,c)										\
	 { __asm__ __volatile__("sti"); irq_handler[n](n,c); __asm__ __volatile__("cli"); }
#else
#define CALL_IRQ_HANDLER(n,c)										\
	 { irq_handler[n](n,c); }
#endif

inline int
intr_irq_test_save(void)
{
	 return cmpxchg32(&irq_save_level, 0, 1);
}

inline int
intr_irq_save(void)
{
	 uint32_t r;
#if 0
	 do
	 {
		  r = irq_save_level;
	 }
	 while (cmpxchg32(&irq_save_level, r, r + 1) != r);
	 return r;
#else
	 uint32_t flags = irq_save();
	 r = irq_save_level ++;
	 irq_restore(flags);
	 return r;
#endif
}

inline void
intr_irq_restore_to(int l)
{
	 irq_save_level = l;
}

inline void
intr_irq_restore_schedule(void)
{
	 uint32_t flags = irq_save();
	 if (irq_save_level == 1)
	 {
		  while (irq_save_bitmap != 0)
		  {
			   int n = bsr(irq_save_bitmap);
			   int c = irq_save_buffer[n];
			   irq_save_buffer[n] = 0;
			   irq_save_bitmap   ^= 1 << n;

			   CALL_IRQ_HANDLER(n, c);
		  }
		  
		  irq_save_level = 0;
		  proc_schedule();
	 }
	 else -- irq_save_level;
	 irq_restore(flags);
}

inline void
intr_irq_restore(void)
{
	 uint32_t flags = irq_save();
	 if (irq_save_level == 1)
	 {
		  while (irq_save_bitmap != 0)
		  {
			   int n = bsr(irq_save_bitmap);
			   int c = irq_save_buffer[n];
			   irq_save_buffer[n] = 0;
			   irq_save_bitmap   ^= 1 << n;

			   CALL_IRQ_HANDLER(n, c);
		  }

		  irq_save_level = 0;
	 }
	 else -- irq_save_level;
	 irq_restore(flags);
}

inline void
intr_irq_process(void)
{
	 uint32_t flags = irq_save();
	 ++ irq_save_level;
	 while (irq_save_bitmap != 0)
	 {
		  int n = bsr(irq_save_bitmap);
		  int c = irq_save_buffer[n];
		  irq_save_buffer[n] = 0;
		  irq_save_bitmap   ^= 1 << n;

		  CALL_IRQ_HANDLER(n, c);
	 }
	 -- irq_save_level;
	 irq_restore(flags);
}

static int
_public_irq_handler(struct trapframe_s *tf)
{
	 int num = tf->num;

	 // if (num == 1) ekf_kprintf("KBD\n");
	 if (irq_handler[num] == NULL)
	 {
		  ekf_intr_irq_eoi(num);
		  return 0;
	 }
	 else if (irq_auto_eoi[num])
	 {
		  ekf_intr_irq_eoi(num);
	 }

	 ++ irq_save_buffer[num];
	 irq_save_bitmap |= 1 << num;

	 if (ekf_intr_trap_from_user(tf))
		  irq_save_level = 0;
	 
	 if (irq_save_level == 0)
	 {
		  ++ irq_save_level;
		  while (irq_save_bitmap != 0)
		  {
			   int n = bsr(irq_save_bitmap);
			   int c = irq_save_buffer[n];
			   irq_save_buffer[n] = 0;
			   irq_save_bitmap   ^= 1 << n;
			   
			   CALL_IRQ_HANDLER(n, c);
		  }
		  
		  -- irq_save_level;
		  proc_schedule();
	 }

	 if (ekf_intr_trap_from_user(tf))
		  uthread_trapframe_switch_back_hook(tf);
	 
	 return 0;
}

static void
print_regs(struct pushregs_s *regs)
{
    ekf_kprintf("  edi  0x%08x\n", regs->edi);
    ekf_kprintf("  esi  0x%08x\n", regs->esi);
    ekf_kprintf("  ebp  0x%08x\n", regs->ebp);
    ekf_kprintf("  oesp 0x%08x\n", regs->esp);
    ekf_kprintf("  ebx  0x%08x\n", regs->ebx);
    ekf_kprintf("  edx  0x%08x\n", regs->edx);
    ekf_kprintf("  ecx  0x%08x\n", regs->ecx);
    ekf_kprintf("  eax  0x%08x\n", regs->eax);
}

static void
print_trapframe(struct trapframe_s *tf)
{
    ekf_kprintf("trapframe at %p\n", tf);
    print_regs(&tf->regs);
    ekf_kprintf("  es   0x----%04x\n", tf->es);
    ekf_kprintf("  ds   0x----%04x\n", tf->ds);
    ekf_kprintf("  err  0x%08x\n", tf->err);
    ekf_kprintf("  eip  0x%08x\n", tf->hw.eip);
    ekf_kprintf("  cs   0x----%04x\n", tf->hw.cs);
    ekf_kprintf("  flag 0x%08x ", tf->hw.eflags);
}

static void
_public_ex_handler(struct trapframe_s *tf)
{
	 ekf_kprintf("EX %d ERR=%d CR2=%08x EIP=%08x\n", tf->num, tf->err, rcr2(), tf->hw.eip);
	 print_trapframe(tf);
	 ekf_readline(NULL);
	 print_backtrace(tf->regs.ebp, tf->hw.eip);

	 proc_legacy_wait();
}

extern char _sys_handler[];

int
intr_init(void)
{
	 irq_save_level = 0;
	 irq_save_bitmap = 0;
	 memset((void *)irq_save_buffer, 0, sizeof(irq_save_buffer));
	 memset((void *)irq_handler, 0, sizeof(irq_handler));
	 memset((void *)irq_auto_eoi, 0, sizeof(irq_handler));

	 int i;
	 for (i = 0; i != IRQ_COUNT; ++ i)
	 {
		  ekf_intr_irq_handler_set(i, _public_irq_handler);
	 }

	 for (i = 0; i != 32; ++ i)
	 {
		  ekf_intr_ex_handler_set(i, _public_ex_handler);
	 }

	 ekf_intr_sys_handler_set((ekf_sys_handler_f)_sys_handler);
}

void
intr_irq_handler_set(int irq, irq_handler_f handler, int auto_eoi)
{
	 irq_handler[irq] = handler;
	 irq_auto_eoi[irq] = auto_eoi;
}
